package com.lechi.web.server.handle;

import com.alibaba.fastjson2.JSON;
import com.lechi.iot.netty.utils.BytesToHexUtil;
import com.lechi.iot.netty.utils.Hex;
import com.lechi.iot.netty.utils.IntegerToByteUtil;
import com.lechi.iot.netty.utils.ModbusCrc16Utils;
import com.lechi.web.common.utils.DateUtils;
import com.lechi.web.constants.Constants;
import com.lechi.web.constants.TcpMessageConstants;
import com.lechi.web.dto.IotDeviceCmdParseDto;
import com.lechi.web.framework.util.GuavaCacheUtil;
import com.lechi.web.framework.websocket.WebSocketServer;
import com.lechi.web.framework.websocket.WebSocketUtil;
import com.lechi.web.system.domain.IotMonitorDataBo;
import com.lechi.web.util.RecordFileUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;

import java.text.DecimalFormat;
import java.util.*;

/**
 * 自定义解码器，将字节流解码为 Java 对象。通常用于接收到数据后进行解码。
 * ，不是所有的handler都可以添加这个注解的，被修饰的handler必须是无状态的，或者说，线程安全的。例如，如果解码器继承自ByteToMessageDecoder，就不能添加该注解。因为ByteToMessageDecoder类里含有成员变量。
 */
@Slf4j
public class ModBusTcpMessageHandler_bak extends ByteToMessageDecoder implements BaseWeatherInboundHandle {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {

        String hostName = getHostName(ctx);
        byte[] msg = new byte[in.readableBytes()];
        in.readBytes(msg);
        String inputDeviceSN = new String(msg).trim();
        if (GuavaCacheUtil.getBiDeviceSnAndIPMap().containsKey(inputDeviceSN)) {
            GuavaCacheUtil.getBiDeviceSnAndIPMap().forcePut(inputDeviceSN, hostName);
            log.error("ModBusTcp 监听接收到设备注册地址：" + hostName + "，注册序列号: " + inputDeviceSN);
            return;
        }
        log.error("ModBusTcp 设备地址：{}, 接收到原始报文:{}", hostName, BytesToHexUtil.bytesToHexString(msg));
        String receiveMessage = Optional.of(msg).map(BytesToHexUtil::bytesToHexString).get();
        byte[] dataBytes = validate(msg, hostName);

        String deviceSNAndAddr = GuavaCacheUtil.getBiDeviceSnAndIPMap().inverse().get(hostName) + "_" + BytesToHexUtil.bytesToHexString(Arrays.copyOf(msg,1));
        var cmdMsgList = GuavaCacheUtil.getCmdParseTableMap().get(deviceSNAndAddr);
        if (cmdMsgList == null || cmdMsgList.isEmpty()) {
            getKeysByValue(hostName).forEach(deviceId -> {
                WebSocketServer.sendInfo(deviceId, JSON.toJSONString(Map.of("record", receiveMessage, "time", DateUtils.getTime())));
            });
            log.error("ModBusTcp 报文中从站地址拼接设备序列号：{} 从缓存中没有获取到，该报文不解析!", deviceSNAndAddr);
            return;
        }
        log.error("ModBusTcp 设备地址：{}, 开始解析报文:{}", hostName, BytesToHexUtil.bytesToHexString(msg));
        List<IotMonitorDataBo> tcpDeviceDataList = new ArrayList<>();
        for (Object object : cmdMsgList) {
            IotDeviceCmdParseDto iotDeviceCmdParseDto = (IotDeviceCmdParseDto) object;
            IotMonitorDataBo iotMonitorPageData = new IotMonitorDataBo();
            BeanUtils.copyProperties(iotDeviceCmdParseDto, iotMonitorPageData);
            try {
                byte[] valueBytes = ArrayUtils.subarray(dataBytes, iotDeviceCmdParseDto.getBeginIndex(), iotDeviceCmdParseDto.getEndIndex());
                byte[] changeValue = buildDataByBitSort(iotDeviceCmdParseDto.getBitSort(), valueBytes);
                String sensorValue = BytesToHexUtil.bytesToHexString(changeValue);
                String v = "";
                Integer digit = Optional.ofNullable(iotDeviceCmdParseDto.getSensorDigit()).orElse(0);
                double intFactor = Optional.ofNullable(iotDeviceCmdParseDto.getIntFactor()).orElse(1d);
                if (Constants.INTEGER_DATA_FORMAT_32.equals(iotDeviceCmdParseDto.getDataFormat())) {
                    v = hexTo32Int(sensorValue, intFactor, digit);
                } else if (Constants.INTEGER_DATA_FORMAT_16.equals(iotDeviceCmdParseDto.getDataFormat())) {
                    v = hexTo16Int(sensorValue, intFactor, digit);
                } else if (Constants.FLOAT_FORMAT_32.equals(iotDeviceCmdParseDto.getDataFormat())){
                    v = hexTo32Float(sensorValue, intFactor, digit);
                } else if (Constants.FLOAT_FORMAT_16.equals(iotDeviceCmdParseDto.getDataFormat())){
                    v = hexTo16Float(sensorValue, intFactor, digit);
                }
                iotMonitorPageData.setValue(Float.parseFloat(v)).setDoubleValue(v).setStringValue(v).setDateTime(new Date());
                insertData(iotMonitorPageData);
                tcpDeviceDataList.add(iotMonitorPageData);
            } catch (Exception e) {
                log.error("ModBusTcp 报文详情：{}, deviceSNAndAddr:{}, 解析异常", receiveMessage, deviceSNAndAddr, e);
                continue;
            }
            alarmRuleChain(iotMonitorPageData);
            WebSocketUtil.sendToMonitorPage(iotMonitorPageData);
            GuavaCacheUtil.getMonitorDataMap().put(iotMonitorPageData.getDeviceId(), iotMonitorPageData.getSensorAliasId(), iotMonitorPageData);
            GuavaCacheUtil.getDeviceIdAndIPMap().put(iotMonitorPageData.getDeviceId(), hostName);
            GuavaCacheUtil.getDeviceLastDated().put(String.valueOf(iotMonitorPageData.getDeviceId()), new Date());
        }
        WebSocketUtil.sendToDevicePage(Constants.MODBUS_RTU, tcpDeviceDataList, receiveMessage);
        RecordFileUtil.producerRecord(tcpDeviceDataList, receiveMessage, "");
    }

    private static byte[] validate(byte[] msg, String hostName) {
        byte[] dataArr = new byte[msg.length - 5];
        byte[] dataLength = new byte[1];
        try {
            System.arraycopy(msg, 3, dataArr, 0, msg.length - 5);
            System.arraycopy(msg, 2, dataLength, 0, 1);
        } catch (Exception e) {
            log.error("ModBusTcp 站点地址：{} 报文数组越界异常{} !", hostName, Hex.toHexString(msg));
            throw new RuntimeException("数组越界异常{}");
        }
        if (dataArr.length != IntegerToByteUtil.bytesToInt(dataLength)) {
            log.error("ModBusTcp 站点地址：{} 报文描述的长短和数据本身长度不一致{} !", hostName, Hex.toHexString(msg));
            throw new RuntimeException("报文描述的长短和数据本身长度不一致");
        }
        byte[] checkCode = {msg[msg.length - 2], msg[msg.length - 1]};
        if (!ModbusCrc16Utils.getCrcString(Arrays.copyOf(msg, msg.length - 2)).equals(Hex.toHexString(checkCode))) {
            log.error("ModBusTcp 站点地址：{} ,报文校验码校验错误: {} !", hostName, Hex.toHexString(msg));
            throw new RuntimeException("报文校验码校验错误");
        }
        return dataArr;
    }

    /**
     * Build data by bit sort byte [ ].
     *
     * @param bitSort    the bit sort
     * @param sourceData the source data
     * @return the byte [ ]
     */
    public static byte[] buildDataByBitSort(String bitSort, byte[] sourceData) {
        if (bitSort == null || sourceData == null) {
            throw new IllegalArgumentException("bitSort 和 sourceData 不能为 null");
        }
        switch (bitSort) {
            case TcpMessageConstants.BA_FORMAT:
                if (sourceData.length < 2) {
                    throw new IllegalArgumentException("sourceData 长度不足，无法进行 BA_FORMAT 排序");
                }
                byte[] bytesBA = new byte[2];
                bytesBA[0] = sourceData[1];
                bytesBA[1] = sourceData[0];
                return bytesBA;
            case TcpMessageConstants.CDAB_FORMAT:
                if (sourceData.length < 4) {
                    throw new IllegalArgumentException("sourceData 长度不足，无法进行 CDAB_FORMAT 排序");
                }
                byte[] bytesCDAB = new byte[4];
                System.arraycopy(sourceData, 2, bytesCDAB, 0, 2);
                System.arraycopy(sourceData, 0, bytesCDAB, 2, 2);
                return bytesCDAB;
            default:
                return sourceData;
        }
    }

    /**
     * 将十六进制字符串转换为32位浮点数
     */
    private static String hexTo32Float(String hexString, double intFactor, Integer digit) {
        int intBits = (int) Long.parseLong(hexString, 16);
        float v = Float.intBitsToFloat(intBits) * (float) intFactor;
        return formatValue(v, digit);
    }

    /**
     * 将十六进制字符串转换为16位浮点数
     */
    private static String hexTo16Float(String hexString, double intFactor, Integer digit) {
        short shortValue = Short.parseShort(hexString, 16);
        float v = shortValue * (float) intFactor;
        return formatValue(v, digit);
    }

    /**
     * 将十六进制字符串转换为32位整型
     */
    private static String hexTo32Int(String hexString, double intFactor, Integer digit) {
        long longValue = Long.parseLong(hexString, 16);
        long v = (long) (longValue * intFactor);
        return formatValue(v, digit);
    }

    /**
     * 将十六进制字符串转换为16位整型
     */
    private static String hexTo16Int(String hexString, double intFactor, Integer digit) {
        short shortValue = Short.parseShort(hexString, 16);
        float v = shortValue * (float) intFactor;
        return formatValue(v, digit);
    }

    private static String formatValue(float value, Integer digit) {
        return new DecimalFormat(Optional.ofNullable(digit)
                .filter(n -> n > 0)
                .map(n -> "0." + "0".repeat(n))
                .orElse("0"))
                .format(value);
    }
}
